package storage.tx;

import server.starDB;
import storage.buffer.Buffer;
import storage.buffer.PageFormatter;
import storage.file.Block;
import storage.tx.concurrency.ConcurrencyMgr;
import storage.tx.recovery.RecoveryMgr;

public class Transaction {
	
	private static int nextTxNum = 0;
	private RecoveryMgr recoveryMgr;
	private ConcurrencyMgr concurMgr;
	private int txnum;
	private BufferList myBuffers = new BufferList();
	
	public Transaction() {
		txnum = nextTxNum();
		recoveryMgr = new RecoveryMgr(txnum);
		concurMgr = new ConcurrencyMgr();
	}
	
	public void commit() {
		recoveryMgr.commit();
		concurMgr.release();
		myBuffers.unpinAll();
		System.out.println("Transaction " + txnum + "committed.");
	}
	
	public void rollback() {
		recoveryMgr.rollback();
		concurMgr.release();
		myBuffers.unpinAll();
		System.out.println("Transaction " + txnum + "rolled back.");
	}
	
	public void recover() {
		starDB.bufferMgr().flushAll(txnum);
		recoveryMgr.recover();
	}
	
	public void pin(Block blk) {
		myBuffers.pin(blk);
	}
	
	public void unpin(Block blk) {
		myBuffers.unpin(blk);
	}
	
	public int getInt(Block blk, int offset)  {
		concurMgr.sLock(blk);
		return myBuffers.getBuffer(blk).getInt(offset);
	}
	
	public String getString(Block blk, int offset)  {
		concurMgr.sLock(blk);
		return myBuffers.getBuffer(blk).getString(offset);
	}
	
	public void setInt(Block blk, int offset, int val)  {
		concurMgr.xLock(blk);
		Buffer b = myBuffers.getBuffer(blk);
		int lsn = recoveryMgr.setInt(b, offset, val);
		b.setInt(offset, val, txnum, lsn);
	}
	
	public void setString(Block blk, int offset, String val)  {
		concurMgr.xLock(blk);
		Buffer b = myBuffers.getBuffer(blk);
		int lsn = recoveryMgr.setString(b, offset, val);
		b.setString(offset, val, txnum, lsn);
	}
	
	public int size(String filename)  {
		Block blk = new Block(filename, -1);
		concurMgr.xLock(blk);
		return starDB.fileMgr().size(filename);
	}
	
	public Block append(String filename, PageFormatter pfmt)  {
		Block blk = new Block(filename, -1);
		concurMgr.xLock(blk);
		Block newblk = myBuffers.pinNew(filename, pfmt);
		unpin(newblk);
		return newblk;
	}
	
	private static synchronized int nextTxNum() {
		nextTxNum++;
		System.out.println("New Transaction: " + nextTxNum);
		return nextTxNum;
	}
}